﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using SubConvert.Core;
using System.Windows.Shell;

namespace SubConvert
{
    internal class ProgramArgs
    {
        public bool KeepOriginalSubs { get; set;}
        public string InputDirectory { get; set; }
        public bool Recursive { get; set; }
        public string Title { get; set; }
        public bool Verbose { get; set; }
        public bool DeleteInputFiles { get; set; }
        public bool DeleteIntermediateFiles { get; set; }
    }


    class Program
    {
        private static StreamWriter stream;

        static void Main(string[] args)
        {
            IEnumerable<ProgramArgs> programArgsList;
            var programArgs = HandleArgs(args);
            if (string.IsNullOrEmpty(programArgs.InputDirectory))
            {
                Console.WriteLine("Please enter input directory [c:\\bluray conversions]: ");
                var inputDirectory = Console.ReadLine();
                if (string.IsNullOrEmpty(inputDirectory))
                {
                    inputDirectory = @"c:\bluray conversions";
                }
                inputDirectory = inputDirectory.Replace('/', '\\');
                if(inputDirectory.ToLower().EndsWith(".xml"))
                {
                    programArgsList = ReadXML(inputDirectory);
                }
                else
                {
                    var defaultXml = inputDirectory.TrimEnd('\\') + "\\subconvert.xml";
                    if (File.Exists(defaultXml))
                    {
                        programArgsList = ReadXML(defaultXml);
                    }
                    else
                    {
                        programArgs.InputDirectory = inputDirectory;
                        programArgsList = new[] { programArgs };
                    }
                }

            }
            else
            {
                programArgsList = new[] {programArgs};
            }
            foreach (var allArgs in programArgsList)
            {
                programArgs.InputDirectory = allArgs.InputDirectory.Replace('/', '\\');
                ProcessDirectory(allArgs);
            }

            Console.WriteLine("Press Any Key to Exit...");
            Console.ReadKey();
            
        }

        private static void ProcessDirectory(ProgramArgs programArgs)
        {
            var files = GetAllFiles(programArgs.InputDirectory, programArgs.Recursive).ToList();

            files.ForEach(Console.WriteLine);
            foreach (var file in files)
            {
                var fileInfo = new FileInfo(file);
                var shortName = fileInfo.Name;
                var fancyName = programArgs.Title;
                if (string.IsNullOrEmpty(fancyName) && fileInfo.Directory != null)
                {
                    fancyName = fileInfo.Directory.Name;
                    fancyName = fancyName.Replace('_', ' ');
                }

                var logFileName = fancyName + ".log";
                var logFileNamePath = Path.Combine(programArgs.InputDirectory, logFileName);
                SpecialWriter.SetLogFile(logFileNamePath);

                var directory = fileInfo.DirectoryName;
                var title = shortName.Split('.')[0];
                var subConvert = new SubConverter();
                var tracks = subConvert.GetTrackInfo(file, programArgs.Verbose);

                tracks = tracks.Where(x => x.TrackType == TrackTypes.Subtitles && x.CodecId == "S_HDMV/PGS");
                if (tracks.Count() == 0)
                {
                    SpecialWriter.WriteLine("No subtitle tracks found");
                    continue;
                }
                SpecialWriter.WriteLine("Found {0} subtitle tracks", tracks.Count());
                foreach (var s in tracks)
                {
                    SpecialWriter.WriteLine("\t Track {0} {1}", s.TrackNumber, s.TrackLanguage.LanguageName);
                }
                var trackNumbers = tracks.Select(x=>x.TrackNumber);
                if (subConvert.ExtractSubtitles(file, title, trackNumbers, directory))
                {
                    subConvert.ConvertSubtitles(title, tracks.ToArray(), directory, programArgs.Verbose);
                    subConvert.MergeTracks(file, title, trackNumbers, directory, fancyName, programArgs.KeepOriginalSubs);
                }
                RemoveFiles(title, trackNumbers, directory, programArgs.DeleteInputFiles, programArgs.DeleteIntermediateFiles);
            }
        }

        private static void RemoveFiles(string title, IEnumerable<string> trackNumbers, string directory, bool deleteInput, bool deleteIntermediate)
        {
            List<string> files = new List<string>();
            foreach (var trackNumber in trackNumbers)
            {
                if (deleteIntermediate)
                {
                    files.Add(string.Format("{0}\\{1}_track{2}.sup", directory, title, trackNumber));
                    files.Add(string.Format("{0}\\{1}_track{2}_exp.idx", directory, title, trackNumber));
                    files.Add(string.Format("{0}\\{1}_track{2}_exp.sub", directory, title, trackNumber));
                }

                if (deleteInput)
                {
                    files.Add(string.Format("{0}\\{1}.mkv", directory, title));
                }
            }

            foreach (var file in files)
            {
                try
                {
                    SpecialWriter.WriteLine("Deleting {0}", file);
                    File.Delete(file);
                }
                catch (Exception e)
                {
                    Console.WriteLine("unable to delete {0}: {1}", file, e);
                }
            }
        }

        private static ProgramArgs HandleArgs(IEnumerable<string> args)
        {
            var argList = new LinkedList<string>(args);
            var programArgs = new ProgramArgs {DeleteIntermediateFiles = true};
            while (argList.First != null)
            {
                switch (argList.First.Value.ToLower())
                {
                    case "--inputdir":
                        argList.RemoveFirst();
                        programArgs.InputDirectory = argList.First.Value;
                        argList.RemoveFirst();
                        break;
                    case "--r":
                        argList.RemoveFirst();
                        programArgs.Recursive = true;
                        break;
                    case "--k":
                        argList.RemoveFirst();
                        programArgs.KeepOriginalSubs = true;
                        break;
                    case "--title":
                        argList.RemoveFirst();
                        programArgs.Title = argList.First.Value;
                        argList.RemoveFirst();
                        break;
                    case "--v":
                    case "--verbose":
                        argList.RemoveFirst();
                        programArgs.Verbose = true;
                        break;
                    case "--delete":
                        argList.RemoveFirst();
                        programArgs.DeleteInputFiles = true;
                        break;
                    case "--keepIntermediate":
                        argList.RemoveFirst();
                        programArgs.DeleteIntermediateFiles = false;
                        break;
                    default:
                        Console.WriteLine("Invalid Parameter {0}", argList.First);
                        Exit();
                        break;

                }
            }
            return programArgs;
        }

        private static void Exit()
        {
            Environment.Exit(1);
        }

        private static IEnumerable<string> GetAllFiles(string inputDirectory, bool recursive)
        {
            List<string> files;
            try
            {
                files = Directory.GetFiles(inputDirectory, "*.mkv").ToList();
            }
            catch (Exception e)
            {
                Console.WriteLine("Error processing {0}", inputDirectory);
                return new List<string>();
            }
            if (recursive)
            {
                var directories = Directory.GetDirectories(inputDirectory);
                foreach (var directory in directories)
                {
                    files.AddRange(GetAllFiles(directory, true));
                }
            }
            return files;
        }

        private static ProgramArgs[] ReadXML(string xmlPath)
        {
            var xDocument = XDocument.Load(xmlPath);
            var directoryList = new List<ProgramArgs>();
            var topElement = xDocument.Element("SubConvert");
            var directories = topElement.Elements().Where(x => x.Name == "Directory");
            foreach (var directory in directories)
            {
                var programArgs = new ProgramArgs();

                programArgs.InputDirectory = directory.Attribute("inputPath").Value;
                programArgs.DeleteInputFiles = Convert.ToBoolean(directory.Attribute("deleteInputFiles").Value);
                programArgs.Verbose = Convert.ToBoolean(directory.Attribute("verbose").Value);
                programArgs.Recursive = Convert.ToBoolean(directory.Attribute("recursive").Value);
                programArgs.Title = directory.Attribute("title") != null ? directory.Attribute("title").Value : string.Empty;
                programArgs.KeepOriginalSubs = Convert.ToBoolean(directory.Attribute("keepOriginalSubs").Value);
                programArgs.DeleteIntermediateFiles =
                    Convert.ToBoolean(directory.Attribute("deleteIntermediateFiles").Value);
                directoryList.Add(programArgs);
            }
            return directoryList.ToArray();
        }
    }
}
